#include "gene.hh"
#include "constants.hh"
#include "mutations.hh"
#include <iostream>
#include <iomanip>
using namespace std;
using namespace evolution;


float Gene::random_weight () { 
    return random_weight_distribution(generator);
}

int ConnectionGene::INNOVATION = 0;
int ConnectionGene::new_innovation_number () {
    return INNOVATION++;
}

NodeType NodeGene::random_type () {
    return static_cast<NodeType>(random_node_type_distribution(generator));
}

Gene::Gene(float weight) : weight(weight) {
}

bool Gene::mutate() {
    if (with_probability(WEIGHT_MUTATION_PROBABILITY)) {
        //cout << "Mutation: changed weight, old=" << fixed << setprecision(3) << weight;
        normal_distribution<float> mutation_distribution(weight, WEIGHT_MUTATION_STDDEV);
        weight = mutation_distribution(generator);
        if (weight < WEIGHT_MIN) weight = WEIGHT_MIN;
        else if (weight > WEIGHT_MAX) weight = WEIGHT_MAX;

        //cout << ", new=" << weight << endl;
        return true;
    }

    return false;
}


NodeGene::NodeGene (int id, float bias, NodeType type)
 : Gene(bias), type(type), id(id)
{

}

ConnectionGene::ConnectionGene(int innovation_number, int source, int target, float weight, bool enabled)
 : Gene(weight), innovation_number(innovation_number), enabled(enabled),
   source(source), target(target)
{
    
}

bool
NodeGene::mutate() {
    if (Gene::mutate()) {
        //cout << "\tnode: " << get_id() << endl;
    }
    if (with_probability(MUTATION_NODE_TYPE_CHANGE_PROBABILITY)) {
       // cout << "Mutation: changed node type\n";
       // cout << "\tnode: " << get_id() << endl;
        type = random_type();
    }
    return false;
}

bool
ConnectionGene::mutate() {
    if (Gene::mutate()) {
        //cout << "\tconnection: " << get_source() << " -> " << get_target() << endl;
    }
    if (with_probability(MUTATION_CONNECTION_TOGGLE_PROBABILITY)) {
        //cout << "Mutation: changed connection enabled state to " 
        //     << (enabled ? "disabled" : "enabled") << endl;
        enabled = !enabled;
    }
    return false;
}



ostream& operator << (ostream& out, NodeGene const& gene) {
    return out << "Gene (no operator<<)\n";
}

ostream& operator << (ostream& out, ConnectionGene const& gene) {
    return out;
}













